/*	--- 生成器 --- 


# 将一个【复杂对象】的【构建】与它的【表示】分离, 使得【同样的构建过程】可以创建【不同的表示】。

----------------------------------------------------

【生成器模式】（Builder）是使用【多个“小型”工厂】来最终创建出【一个完整对象】。

当我们使用【Builder】的时候，一般来说，是因为【创建这个对象】的步骤比较多，每个步骤都需要一个零部件，最终组合成一个完整的对象。

我们仍然以【Markdown】转【HTML】为例，因为直接编写【一个完整的转换器】比较困难，但如果针对类似下面的一行文本：

# this is a heading

转换成HTML就很简单: 

<h1>this is a heading</h1>


因此，我们把【Markdown】转【HTML】看作【一行一行的转换】，【每一行】根据语法，使用【不同的转换器】：

	· 如果以 # 开头，使用【HeadingBuilder转换】；
	· 如果以 > 开头，使用【QuoteBuilder转换】；
	· 如果以 --- 开头，使用【HrBuilder转换】；
	· 其余使用【ParagraphBuilder转换】。


这个【HtmlBuilder】写出来如下： */
public class HtmlBuilder {
	private HeadingBuilder headingBuilder = new HeadingBuilder();
	private HrBuilder hrBuilder = new HrBuilder();
	private ParagraphBuilder paragraphBuilder = new ParagraphBuilder();
	private QuoteBuilder quoteBuilder = new QuoteBuilder();


	public String toHtml(String markdown) { // Builder.build()

		StringBuilder sb = new StringBuilder();

		markdown.lines().forEach( line -> {
			if (line.startsWith("#")) {
				sb.append(headingBuilder.buildHeading(line)).append('\n');
			} else if (line.startsWith(">")) {
				sb.append(quoteBuilder.buildQuote(line)).append('\n');
			} else if (line.startsWith("---")) {
				sb.append(hrBuilder.buildHr(line)).append('\n');
			} else {
				sb.append(paragraphBuilder.buildParagraph(line).append('\n'));
			}
		});

		return sb.toString();
	}
}


/*
注意观察上述代码，【HtmlBuilder】并不是一次性把【整个Markdown】转换为【HTML】，而是【一行一行转换】，

并且，它自己并不会将【某一行】转换为【特定的HTML】，而是根据特性把每一行都“委托”给【一个XxxBuilder】去【转换】，

最后，把【所有转换的结果】组合起来，返回给【客户端】。

这样一来，我们只需要针对【每一种类型】编写【不同的Builder】。

例如，针对【以 # 开头的行】，需要【HeadingBuilder】： */
public class HeadingBuilder {

	public String buildHeading(String line) {
		int n = 0;

		while (line.charAt(0) == '#') {
			n++;
			line = line.substring(1); // 取右substring
		}

		return String.format("<h%d>%s</h%d>", n, line.strip(), n);
	}
}


/*
PS: 实际解析【Markdown】是【带有状态】的，即下一行的语义可能与上一行相关。

这里我们简化了【语法】，把【每一行】视为【可以独立转换】。


======================================================


可见，使用【Builder模式】时，适用于【创建的对象】比较复杂，最好一步一步创建出“零件”，最后再装配起来。

【JavaMail】的【MimeMessage】就可以看作是【一个Builder模式】，只不过【Builder】和【最终产品】合二为一，都是【MimeMessage】： */

Multipart multipart = new MimeMultipart();

// 添加text
BodyPart textpart = new MimeBodyPart();
textpart.setContent(body, "text/html; charset=utf-8");
multipart.addBodyPart(textpart);

// 添加image
BodyPart imagepart = new MimeBodyPart();
imagepart.setFileName(fileName);
imagepart.setDataHandler(new DataHandler(new ByteArrayDataSource(input, "application/octet-stream"))); // Source n. 原始资料
multipart.addBodyPart(imagepart);

MimeMessage message = new MimeMessage(session);

// 设置【发送方地址】
message.setFrom(new InternetAddress("me@example.com"));

// 设置【接收方地址】
message.setRecipient(Message.RecipientType.TO, new InternetAddress("xiaoming@somewhere.com"));

// 设置邮件主题
message.setSubject("Hello", "UTF-8");

// 设置邮件内容为multipart:
message.setContent(multipart);



/*
很多时候，我们可以【简化Builder模式】，以【链式调用】的方式来【创建对象】。

例如，我们经常编写这样的代码： */
StringBuilder builder = new StringBuilder();
builder.append(secure ? "https://" : "http://")
		.append("www.baidu.com")
		.append("/")
		.append("?t=0");

String url = builder.toString();


/*
由于我们经常需要构造URL字符串，可以使用Builder模式编写一个URLBuilder，调用方式如下： */
String url = URLBuilder.builder() // 创建Builder
						.setDomain("www.baidu.com") // 设置domain
						.setScheme("https") // 设置scheme
						.setPath("/"); // 设置路径
						.setQuery(Map.of("a", "123", "q", "K&R"))
						.build(); // 完成build



/*=========================================================


练习

从下载练习：使用【Builder模式】实现【一个URLBuilder】 */
import java.util.HashMap;
import java.util.Map;

public class URLBuilder {
	private String domain;
	private String scheme; // scheme n. 体系,体制
	private String path;
	private Map<String, String> query = new HashMap<>();

	static URLBuilder builder() {
		return new URLBuilder();
	}

	URLBuilder setScheme(String scheme) { 
		this.scheme = scheme;
		return this;
	}

	URLBuilder setDomain(String domain) {
		this.domain = domain;
		return this;
	}

	URLBuilder setPath(String path) {
		this.path = path;
		return this;
	}

	URLBuilder setQuery(Map<String, String> query) {
		this.query.putAll(query); // putAll(Map)
		return this;
	}

	String build() {
		StringBuilder sb = new StringBuilder();

		sb.append(scheme)
		  .append("://")
		  .append(domain);

		if (path != null) {
			sb.append(path);
		}

		if (!query.isEmpty()) {
			sb.append("?");
			
			for (String key : query.keySet()) {
				sb.append(key)
				  .append("=")
				  .append(query.get(key))
				  .append("&");
			}

			return sb.substring(0, sb.length() - 1);
				// sb.length()-1 = 减去最后一个&
		}

		return sb.toString();
	}

	public static void main(String[] args) {
		Map<String, String> query = new HashMap<>();
		query.put("a", "123");  // Map.put(<>, <>)
		query.put("s", "hello");// Map.put(<>, <>)

		String result = URLBuilder.builder()
								  .setDomain("www.baidu.com")
								  .setScheme("https")
								  .setPath("/path")
								  .setQuery(query)
								  .build();

		System.out.println(result);
	}
}




/*
=========================================
#	----- 生成器 の 小结 ----- 

1. Builder模式是为了创建一个复杂的对象，需要多个步骤完成创建，或者需要多个零件组装的场景，且创建过程中可以灵活调用不同的步骤或组件。


总结:
	1. Builder生成器 具有set_Parameter() 和 build_实例() 这2种方法

-----------

模版:
	参考练习 */